#include <Arduino.h>

#include <HttpsOTAUpdate.h>
#include <WIFI/my_wifi.h>
#include <WiFi.h>
#include <HTTP/client/my_client.h>
#include <HTTP/server/my_server.h>
#include <Blinker.h>
#include <BlinkerAPIs/BlinkerAPIs.h>

#ifdef USE_OLED_DRIVER
#include <Wire.h>
#include <SSD1306Wire.h>
#endif

#include <lvgl.h>
#include "lv_porting/lv_porting.h"
#include "lv_ui/lv_ui.h"

#if ARDUINO_USB_CDC_ON_BOOT==1
#define Serial Serial0
#endif

/*硬件类初始化*/
#ifdef USE_OLED_DRIVER
SSD1306Wire OLED(0x3C, 7, 6);
#endif

#define PWR 45


/*任务句柄初始化*/
TaskHandle_t lvgl_timehander_taskhandle;
TaskHandle_t epd_refresh_taskhandle;
TaskHandle_t blinker_taskhandle;
TaskHandle_t pc_poweron_taskhandle;
/*互斥锁初始化*/
// SemaphoreHandle_t LVGL_mutex;
//SemaphoreHandle_t Wifi_mutex=xSemaphoreCreateMutex();
/*任务通信变量*/
EventGroupHandle_t  epd_refresh_event=xEventGroupCreate();//定义：BIT0: lvgl刷新完成，BIT1：无视刷新间隔
//还有一个变量，参见BlinkerAPIs.h

/*Tick类初始化（暂时起这个名字）*/
uint64_t DoMiTree_Tick=0;
uint64_t Clock_Tick=0;

/*全局函数声明*/
void LVGL_timehander_task();

// void API_dataRead(const String &data)
// {
//   log_d("Blinker readString:",data);
//   //Blinker.push("Hello blinker");
// }

void setup() {
  /*
  * 如果连接到ESP32的AP之后经常自动断连，使用WiFi.softAPConfig函数烧录一次，
  * 然后再把processNextRequest和WiFi.softAPConfig一起注释掉，再烧录一次,
  * 这时应该能够稳定和AP连接了，最后把WiFi.softAPConfig注释掉就可以用了，偶有断连
  * 但可用
  * 这可能是在AP模式下扫描热点时导致的连接不稳定
  */
  Serial.begin(115200);
  Serial.setDebugOutput(true);

  Serial.println();
  Serial.println();
  Serial.println();

  pinMode(PWR,OUTPUT);
  pinMode(WIFI_EN,OUTPUT);
  digitalWrite(PWR,HIGH);
  digitalWrite(WIFI_EN,HIGH);


#ifdef USE_OLED_DRIVER
  if (OLED.init())
  {
    Serial.println("Start OLED successfully");
  }
  else
  {
    Serial.println("Start OLED Failed");
  }
  OLED.flipScreenVertically();
  OLED.setFont(ArialMT_Plain_10);
  OLED.clear();
  OLED.setTextAlignment(TEXT_ALIGN_LEFT);
  OLED.setFont(ArialMT_Plain_16);
  OLED.drawString(0, 0, "Left Elec:");
  OLED.display();
#endif

  SPIFSS_Init();
  WiFi_Init();
  Server_Init();

  EPD.begin();
  lv_ui_main();
  if(WiFi.status()==WL_CONNECTED)
  {
    digitalWrite(WIFI_EN,LOW);
    setClock();
    Client_GetDomiTreeElec();
    LVGL_timehander_task();
    xEventGroupSetBits(epd_refresh_event,BIT1);

    Blinker_Init();
  }

  /*Task Configation*/
  /*如果有两个任务与LVGL相关，务必添加互斥锁*/
  void EPD_refresh_task(void *pv);
  void LVGL_timehander_task(void *pv);
  void LVGL_process_task(void *pv);
  void Blinker_handle_task(void *pv);
  void pc_poweron_task(void *pv);

  xTaskCreatePinnedToCore(LVGL_timehander_task,"LVGL timehander task",1024*8,NULL,4,&lvgl_timehander_taskhandle,0);
  xTaskCreatePinnedToCore(EPD_refresh_task,"EPD refresh task",1024*8,NULL,4,&epd_refresh_taskhandle,1);
  //xTaskCreatePinnedToCore(Blinker_handle_task,"Blinker task",1024*8,NULL,4,&blinker_taskhandle,1);
  xTaskCreatePinnedToCore(pc_poweron_task,"pc poweron task",1024*8,NULL,4,&pc_poweron_taskhandle,1);

  //xTaskCreatePinnedToCore(LVGL_process_task,"LVGL process task",1024*8,NULL,5,&lvgl_process_task,1);
  
  
  /*Mutex Configation*/
  //LVGL_mutex=xSemaphoreCreateMutex();//为什么在LVGL_process_task获得互斥锁时会assert failed: xQueueSemaphoreTake queue.c:1545 (( pxQueue ))
  //Wifi_status_mutex=xSemaphoreCreateMutex();

}

void loop() {
  
  static bool _is_connecting_last=false;
  
  /*后续会添加任务定时器函数来取代Tick类函数*/
  if(WiFi.status()==WL_CONNECTED)
  {
    if(millis()>10*1000+DoMiTree_Tick)
    {
      DoMiTree_Tick=millis();

      #ifdef USE_OLED_DRIVER
      OLED.setFont(ArialMT_Plain_16);
      OLED.clear();
      OLED.drawString(0, 0, "Left Elec:");
      #endif
      if(Client_GetDomiTreeElec()==HTTP_CODE_OK)
      {
        #ifdef USE_OLED_DRIVER
        OLED.drawString(0, 30,client_return_elec()); 
        #endif
      }     
      #ifdef USE_OLED_DRIVER
      OLED.display();
      #endif
    }
  
    if(millis()>20*1000+Clock_Tick)
    {
      Clock_Tick=millis();
      getClock();

    }

    Blinker.run();
  }
  /*将为检测函数增加测试网络连通性功能，检测到无网络后警告并提供断开连接选项*/
  if(WiFiConnectionDector()==true)
  {
    _is_connecting_last=true;
  }
  else if(WiFiConnectionDector()==false&&_is_connecting_last==true)
  {
    _is_connecting_last=false;
    Client_GetDomiTreeElec();
    /*LVGL立刻刷新内容*/
    xTaskAbortDelay(&lvgl_timehander_taskhandle);
    /*EPD设置立刻刷新事件*/
    xEventGroupSetBits(epd_refresh_event,BIT1);

    BLINKER_DEBUG.stream(Serial);
    Blinker.begin("d896a387634f","","");
    Blinker.attachData(API_dataRead);
  }
  CilentServer();

  delay(100);
}

void LVGL_timehander_task(void *pv)
{
  /*时间显示相关*/
  char transform[2][5]={0};
  const char weekdays[7][4]={"日","一","二","三","四","五","六"};
  String elec_value,date,time,weekday;
  tm time_info;time_t* nowSecs;

  /*宿舍电量*/
  String* elec_msg;
  /*Tick*/
  static TickType_t xLvglTick=0;
  for(;;)
  {
    /*wifi连接检测,控制wifi标志显示*/
    if(wifi_return_status()==WL_CONNECTED||wifi_return_status()==WL_IDLE_STATUS)
    {
      lv_ui_show_symbol_wifi(true);
      lv_ui_show_warning_text(false);

      /*日期和时间更新*/
      if(*(nowSecs=returnClock())>8 * 3600 * 2)
      {
        gmtime_r(nowSecs, &time_info);

        weekday=weekdays[time_info.tm_wday];

        sprintf(transform[0],"%02d",time_info.tm_mon+1);
        sprintf(transform[1],"%02d",time_info.tm_mday);
        date=(String)(time_info.tm_year+1900)+"-"+transform[0]+"-"+transform[1]+" 星期"+weekday;

        sprintf(transform[0],"%02d",time_info.tm_hour);
        sprintf(transform[1],"%02d",time_info.tm_min);
        time=(String)transform[0]+":"+transform[1];

        //Serial.println(date);
        //Serial.println(time);
        lv_ui_set_date_text(date);
        lv_ui_set_time_text(time);
      }
    }
    else
    {
      lv_ui_show_symbol_wifi(false);
      lv_ui_set_warning_text("网络未连接，请检查网络连接");
      lv_ui_show_warning_text(true);
    }

    /*电量数据和低电量显示*/
    elec_msg=client_return_elec();

    #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG
      Serial.println("在LVGL，返回代码："+*(elec_msg+1));
    #endif
    if(*(elec_msg+1)==(String)HTTP_CODE_OK)
    {
      elec_value=*(elec_msg+0);
      lv_ui_show_warning_text(false);
      lv_ui_set_elec(elec_value);
      if(elec_value.toFloat()<40.0&&elec_value!="null")
      {
        lv_ui_show_low_elec_text(true);
      }
      else
      {
        lv_ui_show_low_elec_text(false);
      }
    }
    else
    {
      lv_ui_set_elec("null");
      lv_ui_set_warning_text("电量获取失败");
      lv_ui_show_low_elec_text(false);   
      lv_ui_show_warning_text(true);
    }
    lv_task_handler();
    xEventGroupSetBits(epd_refresh_event,BIT0);
    xTaskDelayUntil(&xLvglTick,pdMS_TO_TICKS(450));
  }
}

void EPD_refresh_task(void *pv)
{
  static TickType_t disp_partical_tick=0,disp_full_tick=0,tick_set=pdMS_TO_TICKS(1000);
  EventBits_t receive_bits;
  for(;;)
  {
    receive_bits=xEventGroupGetBits(epd_refresh_event);
    if(receive_bits&BIT0)
    {
      if(xTaskGetTickCount()>pdMS_TO_TICKS(1000*60*3)+disp_full_tick)
      {
        disp_full_tick=xTaskGetTickCount();
        EPD.refresh(FULL);
        xEventGroupClearBits(epd_refresh_event,BIT0);
      }
      else if(xTaskGetTickCount()>tick_set+disp_partical_tick||receive_bits&BIT1)
      {
        disp_partical_tick=xTaskGetTickCount();
        EPD.refresh(PARTICAL);
        //Serial.println("EPD refreshed");
  
        xEventGroupClearBits(epd_refresh_event,BIT0|BIT1);
      }  
    }
    vTaskDelay(500);  
  }
}

// void Blinker_handle_task(void *pv)
// {
//   for(;;)
//   {
//     if(WiFi.status()==WL_CONNECTED)
//     {
//       xSemaphoreTake(Wifi_mutex,portMAX_DELAY);
//       Blinker.run();
//       xSemaphoreGive(Wifi_mutex);
//     }
//     vTaskDelay(500);
//   }
// }

void pc_poweron_task(void *pv)
{
  
  for(;;)
  {
    xEventGroupWaitBits(pc_poweron_event,BIT0|BIT1,pdFALSE,pdFALSE,portMAX_DELAY);
    log_d("正在开机...");
    if(WiFi.status()==WL_CONNECTED)
    {
      WiFiUDP udp;
      int i=0;
      char pac[102];
      char mac[6];
      const char mac0[6]={0xD8,0xBB,0xC1,0x9E,0x71,0xEA};  //mac地址0 d8:bb:c1:9e:71:ea
      const char mac1[6]={0x8C,0x1D,0x96,0xEF,0xFD,0x30};  //mac地址1 4c:49:6c:4a:1e:c9 2 8C-1D-96-EF-FD-30
      String Address= "192.168.5.255";
      int Port = 3333;//udp port
      if(xEventGroupGetBits(pc_poweron_event)&BIT0)
      {
        Port = 3333;
        strcpy(mac,mac0);
        xEventGroupClearBits(pc_poweron_event,BIT0);
      }
      else if(xEventGroupGetBits(pc_poweron_event)&BIT1)
      {
        Port = 9;//3332
        strcpy(mac,mac1); 
        xEventGroupClearBits(pc_poweron_event,BIT1);
      }
      else
      {
        xEventGroupClearBits(pc_poweron_event,0xFFFFFFFF);
      }
       //make magicpacket
      for(i=0;i<6;i++)
      {
        pac[i]=0xFF;
      }
      for(i=6;i<102;i+=6)
      {
        memcpy(pac+i,mac,6);
      }
      udp.beginPacket(Address.c_str(), Port);
      udp.write((byte*)pac, 102);//send pac to txbuffer
      udp.endPacket();//biubiubiu

      udp.~WiFiUDP();
      log_d("开机成功!");
    }
    vTaskDelay(500);
  }
}

void LVGL_timehander_task()
{
  /*时间显示相关*/
  char transform[2][5]={0};
  const char weekdays[7][4]={"日","一","二","三","四","五","六"};
  String elec_value,date,time,weekday;
  tm time_info;time_t* nowSecs;

  /*宿舍电量*/
  String* elec_msg;

  /*显示wifi标志*/
  lv_ui_show_symbol_wifi(true);
  lv_ui_show_warning_text(false);

  /*更新日期和时间*/
  if(*(nowSecs=returnClock())>8 * 3600 * 2)
  {
    gmtime_r(nowSecs, &time_info);
    weekday=weekdays[time_info.tm_wday];
    sprintf(transform[0],"%02d",time_info.tm_mon+1);
    sprintf(transform[1],"%02d",time_info.tm_mday);
    date=(String)(time_info.tm_year+1900)+"-"+transform[0]+"-"+transform[1]+" 星期"+weekday;
    sprintf(transform[0],"%02d",time_info.tm_hour);
    sprintf(transform[1],"%02d",time_info.tm_min);
    time=(String)transform[0]+":"+transform[1];
    lv_ui_set_date_text(date);
    lv_ui_set_time_text(time);
  }

  /*电量数据和低电量显示*/
  elec_msg=client_return_elec();
  if(*(elec_msg+1)==(String)HTTP_CODE_OK)
  {
    elec_value=*(elec_msg+0);
    lv_ui_show_warning_text(false);
    lv_ui_set_elec(elec_value);
    if(elec_value.toFloat()<40.0&&elec_value!="null")
    {
      lv_ui_show_low_elec_text(true);
    }
    else
    {
      lv_ui_show_low_elec_text(false);
    }
  }
  else
  {
    lv_ui_set_elec("null");
    lv_ui_set_warning_text("电量获取失败");
    lv_ui_show_low_elec_text(false);   
    lv_ui_show_warning_text(true);
  }

  lv_task_handler();
  xEventGroupSetBits(epd_refresh_event,BIT0);
  vTaskDelay(450);
}